package cn.autumnorange.app.user.consumer.authorization.security.security;

import cn.autumnorange.app.common.rpc.exception.JsonException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class JsonSimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {

  private JsonException jsonException;
  protected final Log logger = LogFactory.getLog(this.getClass());
  private String defaultFailureUrl;
  private boolean forwardToDestination = false;
  private boolean allowSessionCreation = true;
  private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

  public JsonSimpleUrlAuthenticationFailureHandler() {}

  public JsonSimpleUrlAuthenticationFailureHandler(JsonException jsonException) {
    this.jsonException = jsonException;
  }

  public JsonSimpleUrlAuthenticationFailureHandler(
      String defaultFailureUrl, JsonException jsonException) {
    this.jsonException = jsonException;
    this.setDefaultFailureUrl(defaultFailureUrl);
  }

  public void onAuthenticationFailure(
      HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
      throws IOException, ServletException {
    if (this.defaultFailureUrl == null) {
      this.logger.debug("No failure URL set, sending 401 Unauthorized error");
      //            response.sendError(HttpStatus.UNAUTHORIZED.value(),
      // HttpStatus.UNAUTHORIZED.getReasonPhrase());
      if (exception instanceof InternalAuthenticationServiceException) {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        jsonException.jsonException(
            request, response, exception, HttpStatus.INTERNAL_SERVER_ERROR.value());
      } else {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        jsonException.jsonException(request, response, exception, HttpStatus.UNAUTHORIZED.value());
      }
    } else {
      this.saveException(request, exception);
      if (this.forwardToDestination) {
        this.logger.debug("Forwarding to " + this.defaultFailureUrl);
        request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
      } else {
        this.logger.debug("Redirecting to " + this.defaultFailureUrl);
        this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
      }
    }
  }

  protected final void saveException(
      HttpServletRequest request, AuthenticationException exception) {
    if (this.forwardToDestination) {
      request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
    } else {
      HttpSession session = request.getSession(false);
      if (session != null || this.allowSessionCreation) {
        request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
      }
    }
  }

  public void setDefaultFailureUrl(String defaultFailureUrl) {
    Assert.isTrue(
        UrlUtils.isValidRedirectUrl(defaultFailureUrl),
        "'" + defaultFailureUrl + "' is not a valid redirect URL");
    this.defaultFailureUrl = defaultFailureUrl;
  }

  protected boolean isUseForward() {
    return this.forwardToDestination;
  }

  public void setUseForward(boolean forwardToDestination) {
    this.forwardToDestination = forwardToDestination;
  }

  public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
    this.redirectStrategy = redirectStrategy;
  }

  protected RedirectStrategy getRedirectStrategy() {
    return this.redirectStrategy;
  }

  protected boolean isAllowSessionCreation() {
    return this.allowSessionCreation;
  }

  public void setAllowSessionCreation(boolean allowSessionCreation) {
    this.allowSessionCreation = allowSessionCreation;
  }
}
